package com.lam.schedule.common.scheduler.util;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.quartz.JobKey;
import org.quartz.SchedulerException;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lam.common.exception.BusinessException;
import com.lam.common.utils.CollectionUtil;
import com.lam.common.utils.NumberUtil;
import com.lam.common.utils.SpringUtils;
import com.lam.common.utils.StringUtils;
import com.lam.framework.async.AsyncManager;
import com.lam.schedule.common.constant.ScheduleConstant;
import com.lam.schedule.common.scheduler.annotation.TaskTarget;
import com.lam.schedule.common.scheduler.factory.ScheduleManage;
import com.lam.schedule.common.scheduler.model.TargetClass;
import com.lam.schedule.entity.QrtzFiredTriggers;
import com.lam.schedule.entity.QrtzTriggerState;
import com.lam.schedule.entity.QrtzTriggers;
import com.lam.schedule.entity.ScheduleJob;
import com.lam.schedule.mapper.QrtzFiredTriggersMapper;
import com.lam.schedule.mapper.QrtzTriggersMapper;

public class TaskUtils {
	
	private static final Logger logger = LoggerFactory.getLogger(TaskUtils.class);
	
	private static ScheduleManage scheduleManage;
	private static QrtzTriggersMapper qrtzTriggersMapper;
	private static QrtzFiredTriggersMapper qrtzFiredTriggersMapper;
	
	private static ScheduleManage getScheduleManage() {
		return scheduleManage == null?(scheduleManage = SpringUtils.getBean(ScheduleManage.class)):scheduleManage;
	}
	private static QrtzTriggersMapper getQrtzTriggersMapper() {
		return qrtzTriggersMapper == null?(qrtzTriggersMapper = SpringUtils.getBean(QrtzTriggersMapper.class)):qrtzTriggersMapper;
	}
	private static QrtzFiredTriggersMapper getQrtzFiredTriggersMapper() {
		return qrtzFiredTriggersMapper == null?(qrtzFiredTriggersMapper = SpringUtils.getBean(QrtzFiredTriggersMapper.class)):qrtzFiredTriggersMapper;
	}
	
	public static boolean isScheduleClustered() {
		return getScheduleManage().isScheduleClustered();
	}
	
	public static JobKey getJobKey(ScheduleJob scheduleJob) {
		return JobKey.jobKey(scheduleJob.getJobKey(), scheduleJob.getJobGroup());
	}
	
	public static TriggerKey getTriggerKey(ScheduleJob scheduleJob) {
		return TriggerKey.triggerKey(scheduleJob.getJobKey(), scheduleJob.getJobGroup());
	}
	
	public static void setScheduleStatus(List<ScheduleJob> jobList){
		if(CollectionUtil.isEmpty(jobList)) {
			return;
		}
		Map<String, ScheduleJob> jobMap = CollectionUtil.getMap(jobList, t -> t.getJobKey());
		if(isScheduleClustered()) {
			//集群模式下
			String schedulerName;
			try {
				 schedulerName = getScheduleManage().getScheduler().getSchedulerName();
			} catch (SchedulerException e) {
				logger.error("查询任务状态异常", e);
				throw new BusinessException("查询任务状态失败");
			}
	    	Set<String> idList = jobMap.keySet();
	    	QueryWrapper<QrtzTriggers> queryWrapper1 = new QueryWrapper<>();
	    	queryWrapper1.eq("sched_name", schedulerName);
	    	queryWrapper1.in("job_name", idList);
	    	queryWrapper1.select("job_name", "group_concat(trigger_state) as trigger_state");
	    	queryWrapper1.groupBy("job_name");
	    	List<QrtzTriggers> triggerList = getQrtzTriggersMapper().selectList(queryWrapper1);
	    	
	    	QueryWrapper<QrtzFiredTriggers> queryWrapper = new QueryWrapper<QrtzFiredTriggers>();
	    	queryWrapper.eq("sched_name", schedulerName);
	    	queryWrapper.in("job_name", idList);
	    	queryWrapper.select("job_name", "group_concat(state) as state");
	    	queryWrapper.groupBy("job_name");
	    	List<QrtzFiredTriggers> firedList = getQrtzFiredTriggersMapper().selectList(queryWrapper);
	    	
	    	if(CollectionUtil.isNotEmpty(triggerList)) {
	    		setScheduleStatus(jobMap, triggerList);
	    	}
	    	if(CollectionUtil.isNotEmpty(firedList)) {
	    		setScheduleStatus(jobMap, firedList);
	    	}
		}else {
			//单机模式
			try {
				List<ScheduleJob> qlist = getScheduleManage().getAllJob();
				setScheduleStatus(jobMap, qlist);
				List<ScheduleJob> runningList = getScheduleManage().getRunningJob();
				setScheduleStatus(jobMap, runningList);
				
			} catch (SchedulerException e) {
				logger.error("处理定时任务状态异常", e);
			}
		}
		for (ScheduleJob job : jobList) {
			if(!isActive(job) && AsyncManager.isRunning(job.getJobKey())) {
				job.setScheduleStatus(ScheduleConstant.SH_STATUS_RUNNING);
			}
		}
		for (ScheduleJob job : jobList) {
			if(StringUtils.isBlank(job.getScheduleStatus())){
				job.setScheduleStatus(ScheduleConstant.SH_STATUS_NONE);
			}
		}
	}
	
	/**
	 * 设置job调度状态
	 * @param map
	 * @param list
	 */
	private static void setScheduleStatus(Map<String, ScheduleJob> map, List<? extends QrtzTriggerState> list){
		for (QrtzTriggerState entity : list) {
			ScheduleJob job = map.get(entity.getJobKey());
			if(job != null){
				job.setScheduleStatus(entity.getState());
			}
		}
	}
	
	public static boolean isActive(ScheduleJob job){
		return NumberUtil.eq(job.getStatus(), ScheduleConstant.JOB_STATUS_YES);
	}
	
	public static void checkTaskIsRunning(ScheduleJob job){
		
		if(!isActive(job)){
			if(AsyncManager.isRunning(job.getJobKey())){
				logger.error("任务正在异步执行中，不能进行其他操作, jobId:"+job.getJobKey());
				throw new BusinessException("任务正在异步执行中，不能进行其他操作！");
			}
			return;
		}

		if(isScheduleClustered()) {
			//集群模式
			QueryWrapper<QrtzFiredTriggers> queryWrapper = new QueryWrapper<QrtzFiredTriggers>();
	    	queryWrapper.eq("job_name", job.getJobKey()).select("job_name,state");
	    	List<QrtzFiredTriggers> list = getQrtzFiredTriggersMapper().selectList(queryWrapper);
	    	if(CollectionUtil.isNotEmpty(list) && ScheduleConstant.SH_STATUS_EXECUTING.equals(list.get(0).getState())){
				logger.error("任务正在执行中，不能进行其他操作, jobId:"+job.getJobKey());
				throw new BusinessException("任务正在执行中，不能进行其他操作！");
			}
	    	return;
		}
		
		//单机模式
		try {
			if(getScheduleManage().isRunning(job)){
				logger.error("任务正在执行中，不能进行其他操作, jobId:"+job.getJobKey());
				throw new BusinessException("任务正在执行中，不能进行其他操作！");
			}
		} catch (SchedulerException e) {
			logger.error("检查任务是否正在执行时出现异常, jobId:"+job.getJobKey(), e);
			throw new BusinessException("检查任务是否正在执行时出现异常");
		}
	}
	
	public static void setDefaultVal(ScheduleJob scheduleJobEntity){
		if(scheduleJobEntity.getConcurrentType() == null){
			scheduleJobEntity.setConcurrentType(ScheduleConstant.CONCURRENT_TYPE_N);
		}
		if(scheduleJobEntity.getStatus() == null){
			scheduleJobEntity.setStatus(ScheduleConstant.JOB_STATUS_YES);
		}
		if(StringUtils.isBlank(scheduleJobEntity.getJobGroup())){
			scheduleJobEntity.setJobGroup(ScheduleConstant.DEFAULT_GROUP);
		}
	}
	
	/**
	 * 扫描有TaskTarget注解的所有类
	 * @param packageName
	 * @return
	 */
	public static List<TargetClass> scanTargetClass(String packageName){
		if(StringUtils.isBlank(packageName)) {
    		return Collections.emptyList();
    	}
    	TargetClass targetClass;
    	Set<Class<?>> classList = new HashSet<>();
    	for (String pt : packageName.split(",")) {
    		classList.addAll(SpringUtils.scanClass(pt, TaskTarget.class));
		}
    	
    	List<TargetClass> list = new ArrayList<>();
		for (Class<?> clazz : classList) {
			targetClass = new TargetClass(clazz.getName());
			Method[] mList = clazz.getMethods();
			for (Method method : mList) {
				if(method.getAnnotation(TaskTarget.class) != null) {
					targetClass.addMethod(method.getName());
				}
			}
			if(CollectionUtil.isNotEmpty(targetClass.getMethods())) {
				list.add(targetClass);
			}
		}
		return list;
	}

}
